<?php

/*
Plugin to get data from Google Refine to display edit history on a Google Refine "Project"

This class posts a small JSON string of authorization data to the Google Refine server.
*/

class TableMetadata{
    
    public $metaDirectoryPath; //full path to the metadata directory
    public $refineURI; //URI for Google Refine. Use the local host, not the reverse proxy for speed
    public $refineHumanURI; //The "human readable/accessible" URI to Google Refine, also used for sending requests to the reverse proxy, not Google refine itself
    public $authHashPrefix; //authorization prefix for interacting with Google Refine reverse proxy server
    
    public $currentUserID; //ID for the current user
    public $currentUserName; //current user name
    public $currentUserRole; //role of the current user
    public $currentMetadata; //metadata for the current Google Refine table / project
    
    //general count information for general descriptive metadata
    public $descriptionsDone; //number of general dataset descriptive fields completed
    public $numberRequiredDescript; //the number of required general descriptive metadata fields
    public $descriptionFields; //general dataset metadata description fields
    
    //general count information for column description / documentation
    public $columnsDocumented; //number of columns with documentation
    public $numberColumns; //total number of columns to document
    
    public $countNumOtherIssues; //total number of "other" issues
    public $countNumOtherIssuesDone; //total number of other issue resolved.
    
    public $issueProject_id; //id for the Issue Tracker project id, this is used in the issue tracker and is not to be confused by Google Refine's "project id"
    public $GRprojectID; //id for google refine project
    public $refineName; //name of the table / Google Refine project
    public $refineModified; //last time the project was updated in Google Refine
    public $refineRowCount; //total number of rows for the Google Refine project / table
    public $refineColArray; //array of project / table columns reported by Google Refine
    public $refineColOther; //array of other metadata for table columns reported by Google Refine
    
    const GRefineEditRole = "editing / cleaning (Google Refine)"; //name of the role for editing in Google Refine
    const metadataEditRole = "Dataset descriptive metadata"; //name of the role for editing and providing general dataset descriptive
    const fieldEditRole = "Field / column metadata documentation"; //name of the role for editing and providing metadata about fields / columns
    const directoryType = "metadata"; //location of table metadata, stored as JSON files
    
    public function get_metadata(){
        
        $this->columnsDocumented = 0;
        $JSONstore = new JSONstore;
        $JSONstore->setDirectory(self::directoryType);
        
        $tableMetadata = $JSONstore->read_parse_json($this->GRprojectID);
        if(!$tableMetadata){
            $this->currentMetadata = false;
            //echo "tried ID = ".$this->GRprojectID;
        }
        else{
            //we've got a table metadata file.
            $this->currentMetadata = $tableMetadata;
            if($tableMetadata["issueIDs"]["editResolved"] == false){
                //the table is still being edited, some current data about the table
                
                //echo "it's false!";
                
                $updateNeeded = false;
                if($this->get_title_update()){
                    //get current title
                    $updateNeeded = false;
                    if(!isset( $tableMetadata["name"])){
                        $updateNeeded = true;
                    }
                    else{
                        if($tableMetadata["name"] != $this->refineName){
                            $updateNeeded = true;
                        }
                    }
                    
                    $tableMetadata["name"] = $this->refineName;
                    $tableMetadata["refineModified"] = $this->refineModified; 
                }
                
                //get the total number of rows
                if($this->get_total_number_rows()){
                    
                    if(!isset( $tableMetadata["refineRowCount"])){
                        $updateNeeded = true;
                    }
                    else{
                        if( $tableMetadata["refineRowCount"] != $this->refineRowCount){
                             $updateNeeded = true;
                        }
                    }
                    
                    $tableMetadata["refineRowCount"] = $this->refineRowCount;
                }
                
                if($updateNeeded){
                    $this->currentMetadata = $tableMetadata;
                    $json = json_encode($tableMetadata, 1);
                    $JSONstore->save_json($this->GRprojectID, $json);
                }
                
            }
            
        }
        
    }
    
    
     
    //counts the number of columns with metadata documentation
    public function count_column_documentation(){
        
        $tableMetadata = $this->currentMetadata;
        $this->columnsDocumented = 0;
        if(isset($tableMetadata["columns"])){
            foreach($tableMetadata["columns"] as $colArray){
                if(isset($colArray["note"])){
                    $this->columnsDocumented ++;
                }
            }
            $this->numberColumns = count($tableMetadata["columns"]);
        }
        else{
            $this->numberColumns = false;
            if($this->get_columns()){
                $columnArray = $this->refineColArray;
                $this->numberColumns = count($columnArray);
            }
        }
    }
    
    

    public function create_dataset_metadata_file($activeProjectUsers, $allIssues, $GRprojectID){
    
        //creates a new dataset metadata JSON file when a Google Refine project (dataset) is first accessioned into the Issue Tracker    
        $tableMetadata = array();
        $tableMetadata["issueProjectID"] = $this->issueProject_id;
        $tableMetadata["issueIDs"] = array();
        $tableMetadata["editors"] = array();

        //add user data for people who edited in Google Refine
        if(is_array($activeProjectUsers)){
            foreach($activeProjectUsers as $userIDKey => $userArray){
                if(!array_key_exists($userIDKey, $tableMetadata["editors"])){
                    $tableMetadata["editors"][$userIDKey] = array(  "userID" => $userIDKey,
                                                                    "userName" => $userArray["userName"],
                                                                    "roles" => array(self::GRefineEditRole)
                                                                    );
                }
                else{
                    if(!in_array(self::GRefineEditRole, $tableMetadata["editors"][$userIDKey]["roles"])){
                        $tableMetadata["editors"][$userIDKey]["roles"][] = self::GRefineEditRole;
                    }
                }
            }
        }
            
        //get ids for important issue numbers
        $tableMetadata["issueIDs"] = $allIssues;
        
        //get project title
        if($this->get_title_update()){
            $tableMetadata["name"] = $this->refineName;
            $tableMetadata["refineModified"] = $this->refineModified;
        }
        
        $this->currentMetadata = $tableMetadata;
        $JSONstore = new JSONstore;
        $JSONstore->setDirectory(self::directoryType);
        $json = json_encode($this->currentMetadata, 1);
        $JSONstore->save_json($GRprojectID, $json);
        
    }
    
    function add_User_Role($userIDkey, $userName, $roleType){
        
        $newRole = false; 
        $roleArray = array("edit" => self::GRefineEditRole,
                           "meta"=> self::metadataEditRole,
                           "field" => self::fieldEditRole);
        
        if(array_key_exists($roleType, $roleArray)){
            $actRole = $roleArray[$roleType];
        }
        else{
            $actRole = false;
        }
        
        if($actRole != false){
            //stores user information for someone who provied an editing or documentation role
            $tableMetadata = $this->currentMetadata;   
            if(isset($tableMetadata["editors"][$userIDkey])){
                if(!in_array($actRole, $tableMetadata["editors"][$userIDkey]["roles"])){
                    $tableMetadata["editors"][$userIDkey]["roles"][] = $actRole;
                    $newRole = true;  
                }
            }
            else{
                $tableMetadata["editors"][$userIDkey] = array(  "userID" => $userIDkey,
                                                                "userName" => $userName,
                                                                "roles" => array($actRole));
                $newRole = true; 
            }
            
            
            if($newRole){
                $this->currentMetadata = $tableMetadata;
            }
        }
        
        return $newRole; 
        
    }//end function
    
    function mark_editIssue_resolution($resolution){
        //this is a cool function! It marks a dataset as resolved in the issue tracker
        $tableMetadata = $this->currentMetadata;
        $authorized = sha1($this->authHashPrefix.$this->GRprojectID); //secure way of sending authorization, without revealing password prefix.
        $message = array(   "authorized" => $authorized,
                            "project" => $this->GRprojectID,
                            "editID" => $tableMetadata["issueIDs"]["editIssue"],
                            "editResolved" => $resolution
		 );
        
        $HTTPclient = new HTTPclient;
	
	$HTTPclient->refineBaseURI = $this->refineHumanURI;
	$HTTPclient->postActionURI = "/login-check-edit-issue";
	$HTTPclient->authorized = $authorized;
	$HTTPclient->message = $message; //json data as a string
	$HTTPclient->postData(); //send the post!
	
	
	if($HTTPclient->success != false){
            //success! save the edit resolution locally
             

            $tableMetadata = $this->currentMetadata; 
            $tableMetadata["issueIDs"]["editResolved"] = $resolution;
            
            //add users as editors of the dataset from records kept at Google Refine reverse proxy server
            $projectEditorsUsers = $this->get_gr_userData(); //people who edited the project   
            if(is_array($projectEditorsUsers)){
                foreach($projectEditorsUsers as $userIDkey => $userDataArray){
                    if(isset($tableMetadata["editors"][$userIDkey])){
                        if(!in_array(self::GRefineEditRole, $tableMetadata["editors"][$userIDkey]["roles"])){
                            $tableMetadata["editors"][$userIDkey]["roles"][] = self::GRefineEditRole;
                        }
                    }
                    else{
                        $tableMetadata["editors"][$userIDkey] = array("userID" => $userDataArray["userID"],
                                                                      "userName" => $userDataArray["userName"],
                                                                      "roles" => array(self::GRefineEditRole));
                    }
                }
            }
            
            $this->currentMetadata = $tableMetadata;
            $JSONstore = new JSONstore;
            $JSONstore->setDirectory(self::directoryType);
            $json = json_encode($this->currentMetadata, 1);
            $JSONstore->save_json($this->GRprojectID, $json);
        }
        else{
            echo "<h1>CRAP! ".$HTTPclient->errorMessage."</h1>";
            
        }
        
    }
    
    
    function mark_fieldDes_resolution($resolveField, $resolution){
        //this is a cool function! It marks a dataset as resolved in the issue tracker
        $tableMetadata = $this->currentMetadata;
        $tableMetadata["issueIDs"][$resolveField] = $resolution;
        $this->currentMetadata = $tableMetadata;
        $JSONstore = new JSONstore;
        $JSONstore->setDirectory(self::directoryType);
        $json = json_encode($this->currentMetadata, 1);
        $JSONstore->save_json($this->GRprojectID, $json);
    }
    
    function mark_otherIssue_resolution($issueID, $resolution){
        //this is a cool function! It marks a dataset as resolved in the issue tracker
        $tableMetadata = $this->currentMetadata;
        $tableMetadata["issueIDs"]["others"][$issueID]["resolved"] = $resolution;
        $this->currentMetadata = $tableMetadata;
        $JSONstore = new JSONstore;
        $JSONstore->setDirectory(self::directoryType);
        $json = json_encode($this->currentMetadata, 1);
        $JSONstore->save_json($this->GRprojectID, $json);
    }
    
    function count_otherIssue_done(){
        $tableMetadata = $this->currentMetadata;
        $otherIssues = $tableMetadata["issueIDs"]["others"];
        $this->countNumOtherIssues = count($otherIssues);
        $this->countNumOtherIssuesDone = 0;
        if(is_array($otherIssues)){
            foreach($otherIssues as $bugID => $statusArray){
                if($statusArray["resolved"]){
                    $this->countNumOtherIssuesDone++;
                }
            }
        }
    }
    
    function check_updateIssue($refIssue = false, $bugResolved = false){
    //checks to see if the referrin issue is new, if not, return false.
        $tableMetadata = $this->currentMetadata;
        $newIssue = false;
        if($refIssue != false || isset($_GET["issue"])){
            if(!$refIssue){
                $refIssue = $_GET["issue"];
            }
            if($refIssue != $tableMetadata["issueIDs"]["editIssue"] && $refIssue != $tableMetadata["issueIDs"]["describeIssue"] && $refIssue != $tableMetadata["issueIDs"]["fieldsIssue"]){
               
                if(isset($tableMetadata["issueIDs"]["others"][$refIssue])){
                    $oldResolved = $tableMetadata["issueIDs"]["others"][$refIssue];
                    if($oldResolved != $bugResolved){
                        $newIssue = $refIssue;
                    }
                }
                else{
                    $newIssue = $refIssue;
                }
                
                $tableMetadata["issueIDs"]["others"][$refIssue] = array("id" => $refIssue, "resolved" => $bugResolved);
            }
        }
        
        if($newIssue != false){
            //sae the new issue link
            $this->currentMetadata = $tableMetadata;
            $JSONstore = new JSONstore;
            $JSONstore->setDirectory(self::directoryType);
            $json = json_encode($this->currentMetadata, 1);
            $JSONstore->save_json($this->GRprojectID, $json);
            
        }
        
        
        return $newIssue;
    }
    
    
    function add_descriptions(){
        
        $tableMetadata = $this->currentMetadata;
        $datasetMetadata = $this->get_descriptive_fields();
        
        if(!isset($tableMetadata["description"])){
            
            $tableMetadata["description"] = array();
            foreach($datasetMetadata["fields"] as $fieldKey => $fieldArray){
                if($fieldArray["required"]){
                    $tableMetadata["description"][$fieldKey] = array(
                                                        "name" => $fieldArray["name"],
                                                        "values" => false);
                }
            }
            
            $this->currentMetadata = $tableMetadata;
            $JSONstore = new JSONstore;
            $JSONstore->setDirectory(self::directoryType);
            $json = json_encode($this->currentMetadata, 1);
            $JSONstore->save_json($this->GRprojectID, $json);
        }
        
    }
    
    
    function count_description_done(){
        //counts the number of complete required metadata fields
        
        $tableMetadata = $this->currentMetadata;
        if(!isset($this->descriptionFields)){
            $datasetMetadata = $this->get_descriptive_fields();
        }
        else{
            $datasetMetadata = $this->descriptionFields;
        }
        $this->numberRequiredDescript = 0;
        $this->descriptionsDone = 0;
        foreach($datasetMetadata["fields"] as $fieldKey => $fieldArray){
            if($fieldArray["required"]){
                $this->numberRequiredDescript ++;
                $valuePresent = false;
                if(isset($tableMetadata["description"])){
                    if(isset($tableMetadata["description"][$fieldKey]["values"])){
                        $actualDescription = $tableMetadata["description"][$fieldKey]["values"];
                        if(is_array($actualDescription)){
                            foreach($actualDescription as $actDesArray){
                                if(is_array($actDesArray)){
                                    if(isset($actDesArray["text"])){
                                        $valuePresent = true;    
                                    }
                                }
                            }
                        }
                    }
                }
                if($valuePresent){
                    $this->descriptionsDone++;
                }
            }
        }
        
    }//end function
    
    
    
    
    function get_descriptive_fields(){
        //gets an array of the fields to describe a dataset
        $MetadataStandards = new MetadataStandards;
        $datasetMetadata = $MetadataStandards->get_datasetMetadata();
        $this->descriptionFields = $datasetMetadata;
        
        return $datasetMetadata;
    }
    
    
    function add_columns(){
        //adds data about columns from Google refine
        
        if($this->get_columns()){
            $tableMetadata = $this->currentMetadata;
            if(!isset($tableMetadata["columns"])){
                $tableMetadata["columns"] = $this->refineColArray;
            }
            else{
                //since there was already some column documentation, let's not throw it out
                
                $newColumns = $this->refineColArray;
                $oldColumns = $tableMetadata["columns"];
                foreach($oldColumns as $oldCol){
                    $addData = array();
                    if(isset($oldCol["note"])){
                        $addData["note"] = $oldCol["note"];
                    }
                    if(isset($oldCol["unit"])){
                        $addData["unit"] = $oldCol["unit"];
                    }
                    if(isset($oldCol["concept"])){
                        $addData["concept"] = $oldCol["concept"];
                    }
                    
                    if(count($addData)>0){
                        
                        //add the old documentation data to the appropriate column
                        $originalName = $oldCol["originalName"];
                        $newNewColumns = array();
                        foreach($newColumns as $newCol){
                            if($newCol["originalName"] == $originalName){
                                foreach($addData as $key => $vals){
                                    $newCol[$key] = $vals;
                                }
                            }
                            $newNewColumns[] = $newCol;
                        }
                        $newColumns = $newNewColumns;
                        unset($newNewColumns);
                    }
                    
                }//end loop through old documentation
                
                $tableMetadata["columns"] = $newColumns;
            }
            
            
            $this->currentMetadata = $tableMetadata;
            $JSONstore = new JSONstore;
            $JSONstore->setDirectory(self::directoryType);
            $json = json_encode($this->currentMetadata, 1);
            $JSONstore->save_json($this->GRprojectID, $json);
        } 
        
    }
    
    
    
    function get_columns(){
        
        $output = false;
        $uri = $this->refineURI."/command/core/get-models?project=".$this->GRprojectID;
        @$json = file_get_contents($uri);
        if($json != false){
            $meta = json_decode($json, 1);
            $this->refineColArray = $meta["columnModel"]["columns"];
            $additionalData = array();
            if(isset($meta["columnModel"]["keyCellIndex"])){
                $additionalData["keyCellIndex"] = $meta["columnModel"]["keyCellIndex"];
                $additionalData["keyColumnName"] = $meta["columnModel"]["keyColumnName"];
                $additionalData["columnGroups"] = $meta["columnModel"]["columnGroups"];
                $this->refineColOther = $additionalData;
            }
            else{
                $this->refineColOther = false;
            }
            $output = true;
        }
        
        return $output ; //returns true if we got our data
        
    }
    
    
    
    function get_title_update(){
        //get the Google Refine project name and last update
        
        $output = false;
        $uri = $this->refineURI."/command/core/get-project-metadata?project=".$this->GRprojectID;
        @$json = file_get_contents($uri);
        if($json != false){
            $meta = json_decode($json, 1);
            $this->refineName = $meta["name"];
            $this->refineModified = $meta["modified"];
            $output = true;
        }
        
        return $output ; //returns true if we got our data
    }
    
    
    function get_total_number_rows(){
        //get the total number of rows
        
        $output = false;
        $uri = $this->refineURI."/command/core/get-rows?start=0&limit=1&project=".$this->GRprojectID;
        @$json = file_get_contents($uri);
        if($json != false){
            $rowArray = json_decode($json, 1);
            $this->refineRowCount = $rowArray["total"];
            $output = true;
        }
        
        return $output ;
    }
    
    
    function get_gr_userData(){
    //HTTP get request to get project user data from the Google Refine reverse proxy server    
        $output = false;
        $authorization = sha1($this->authHashPrefix.$this->GRprojectID);
        $requestURI = $this->refineHumanURI."/login-check-project?auth=".$authorization."&grProject=".$this->GRprojectID;
        //echo $requestURI;
        @$json = file_get_contents($requestURI);
        if($json != false){
            $userData = json_decode($json, true);
            if(is_array($userData)){
                $output = array();
                $output = $userData["users"];
            }
        }
        return $output;
    }

    

}//end class

?>